package com.kmxd.ams.app.system;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Sets;
import com.kmxd.ams.app.event.AmsEventPublisher;
import com.kmxd.ams.client.event.log.LoginLogEvent;
import com.kmxd.ams.client.system.*;
import com.kmxd.ams.client.system.dto.ChangePasswordDTO;
import com.kmxd.ams.client.system.dto.LoginDTO;
import com.kmxd.ams.client.system.vo.AuthPermissionVO;
import com.kmxd.ams.client.system.vo.LoginVO;
import com.kmxd.ams.client.system.vo.RouteItemVO;
import com.kmxd.ams.client.system.vo.SysDeptVO;
import com.kmxd.ams.core.common.bo.LoginUser;
import com.kmxd.ams.core.common.bo.SysRoleBO;
import com.kmxd.ams.core.common.bo.SysUserBO;
import com.kmxd.ams.core.common.constant.CacheConst;
import com.kmxd.ams.core.common.constant.ConfigConst;
import com.kmxd.ams.core.common.constant.Constants;
import com.kmxd.ams.core.common.constant.ErrorCode;
import com.kmxd.ams.core.common.enums.DelFlagEnum;
import com.kmxd.ams.core.common.enums.GrantTypeEnum;
import com.kmxd.ams.core.common.enums.StatusEnum;
import com.kmxd.ams.core.common.enums.UserPlatformEnum;
import com.kmxd.ams.core.exception.BizTipException;
import com.kmxd.ams.core.util.RedisUtils;
import com.kmxd.ams.core.util.SecurityUtil;
import com.kmxd.ams.infra.system.entity.SysRole;
import com.kmxd.ams.infra.system.entity.SysUser;
import com.kmxd.ams.infra.system.mapper.SysUserMapper;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 用户实现类
 *
 * @author admin
 */
@Slf4j
@Service
@AllArgsConstructor
public class SysUserLoginSvcImpl implements ISysUserLoginSvc {

  private final AmsEventPublisher amsEventPublisher;
  private SysUserMapper sysUserMapper;
  private ISysMenuSvc sysMenuSvc;
  private ISysRoleSvc sysRoleSvc;
  private ISysCommonSvc sysCommonSvc;
  private ISysDeptSvc sysDeptSvc;
  @Autowired private RedisUtils redisUtils;
  private ISysConfigSvc sysConfigSvc;

  @Override
  public LoginVO login(LoginDTO req) {
    LoginVO loginVO = new LoginVO();
    loginVO.setMultiCrops(false);
    SysUser sysUser = null;
    if (StrUtil.isBlank(req.getUsername())) {
      amsEventPublisher.publishEvent(
          new LoginLogEvent("登录", 2, req.getUsername(), "账号密码登录", "用户名不能为空"));
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "用户名不能为空");
    }
    if (StrUtil.isBlank(req.getPassword())) {
      amsEventPublisher.publishEvent(
          new LoginLogEvent("登录", 2, req.getUsername(), "账号密码登录", "密码不能为空"));
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "密码不能为空");
    }
    sysUser =
        sysUserMapper.selectOne(
            Wrappers.lambdaQuery(SysUser.class)
                .eq(SysUser::getAccount, req.getUsername())
                .eq(SysUser::getDelFlag, DelFlagEnum.NO.getCode()));

    // 获取用户IP信息
    HttpServletRequest request =
        ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
    String ipAddress = "0.0.0.0";

    if (Objects.isNull(sysUser)) {
      log.info("登录用户：{}不存在", req.getUsername());
      amsEventPublisher.publishEvent(
          new LoginLogEvent("登录", 2, req.getUsername(), "账号密码登录", "登录用户不存在"));
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "登录用户不存在！");
    }
    // 查询用户部门
    SysDeptVO sysDept = sysDeptSvc.view(sysUser.getDeptId());

    if (ObjectUtil.equals(sysUser.getStatus(), StatusEnum.NO.getCode())) {
      log.info("登录用户：{}已被停用", req.getUsername());
      amsEventPublisher.publishEvent(
          new LoginLogEvent("登录", 2, req.getUsername(), "账号密码登录", "账号已被停用"));
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "对不起，您的账号已被停用！");
    }
    if (ObjectUtil.equals(sysUser.getLockFlag(), StatusEnum.NO.getCode())) {
      log.info("登录用户：{}已被锁定", req.getUsername());
      amsEventPublisher.publishEvent(
          new LoginLogEvent("登录", 2, req.getUsername(), "账号密码登录", "账号已被锁定"));
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "对不起，您的账号已被锁定！");
    }

    String key = CacheConst.REDIS_PREFIX + "login:" + sysUser.getIp();
    redisUtils.del(key);

    log.info("登录用户：{}", req.getUsername());
    SysUserBO sysUserBO = BeanUtil.copyProperties(sysUser, SysUserBO.class);
    // 查询角色列表
    List<SysRole> sysRoleList = sysRoleSvc.listSysRoleByUserId(sysUser.getId());
    // 设置角色列表
    sysUserBO.setRoles(
        sysRoleList.stream()
            .map(item -> BeanUtil.copyProperties(item, SysRoleBO.class))
            .collect(Collectors.toList()));
    // 查询权限标识
    Set<String> permissions = Sets.newHashSet();
    if (SecurityUtil.isRootGroup(
        sysRoleList.stream().map(SysRole::getRoleCode).collect(Collectors.toSet()))) {
      permissions.add(Constants.ALL_PERMISSION);
    } else {
      permissions = sysMenuSvc.getPermissionsByUserId(sysUser.getId());
    }
    // 登录
    StpUtil.login(sysUser.getId());
    StpUtil.checkActivityTimeout();
    LoginUser loginUser = new LoginUser();
    loginUser.setToken(StpUtil.getTokenValue());
    loginUser.setUserPlatform(UserPlatformEnum.WEB.getCode());
    loginUser.setGrantType(req.getGrantType());
    loginUser.setSysUser(sysUserBO);
    if (req.getGrantType().equals(GrantTypeEnum.USERNAME_PASSWORD.getCode())) {
      loginUser.setUsername(req.getUsername());
    }
    if (req.getGrantType().equals(GrantTypeEnum.MOBILE_CODE.getCode())) {
      loginUser.setUsername(req.getMobile());
    }
    if (ObjectUtil.isNotNull(sysDept)) {
      loginUser.setDeptId(sysDept.getId());
      loginUser.setDeptName(sysDept.getName());
    }
    // 查询角色列表 -- 冗余数据，方便使用
    List<SysRoleBO> roleInfoVOList =
        sysRoleList.stream()
            .map(
                item -> {
                  SysRoleBO roleBO = new SysRoleBO();
                  roleBO.setRoleCode(item.getRoleCode());
                  roleBO.setRoleName(item.getRoleName());
                  roleBO.setValue(item.getRoleCode());
                  roleBO.setId(item.getId());
                  roleBO.setRoleType(item.getRoleType());
                  roleBO.setDataScope(item.getDataScope());
                  return roleBO;
                })
            .collect(Collectors.toList());
    loginUser.setUserId(sysUser.getId().toString());
    loginUser.setUsername(sysUser.getAccount());
    loginUser.setRealName(sysUser.getRealname());
    loginUser.setAvatar(sysUser.getAvatar());
    loginUser.setLoginTime(new Date());
    loginUser.setRoles(roleInfoVOList);
    loginUser.setIpAddress(ipAddress);
    loginUser.setLoginLocation("内网");
    loginUser.setBrowser(userAgent.getBrowser().getName());
    loginUser.setOs(userAgent.getOs().getName());
    loginUser.setPermissions(permissions);

    String conf = sysConfigSvc.getConfigContentByKey(ConfigConst.ARCH_BTN_CONTROL);
    loginUser.setArchAddBtnControl(StrUtil.isNotEmpty(conf) && StrUtil.equals("1", conf) ? 1 : 0);

    // 设置用户信息
    StpUtil.getTokenSession().set(Constants.LOGIN_USER_KEY, loginUser);
    loginVO.setToken(StpUtil.getTokenValue());
    return loginVO;
  }

  @Override
  public void changePassword(ChangePasswordDTO req) {

    if (StrUtil.isBlank(req.getUsername())) {
      amsEventPublisher.publishEvent(
          new LoginLogEvent("修改密码", 2, req.getUsername(), "修改密码", "用户名不能为空"));
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "用户名不能为空");
    }
    if (StrUtil.isBlank(req.getPassword())) {
      amsEventPublisher.publishEvent(
          new LoginLogEvent("修改密码", 2, req.getUsername(), "修改密码", "密码不能为空"));
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "密码不能为空");
    }
    if (StrUtil.isBlank(req.getPassword())) {
      amsEventPublisher.publishEvent(
          new LoginLogEvent("修改密码", 2, req.getUsername(), "修改密码", "新密码不能为空"));
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "新密码不能为空");
    }
    SysUser sysUser =
        sysUserMapper.selectOne(
            Wrappers.lambdaQuery(SysUser.class)
                .eq(SysUser::getAccount, req.getUsername())
                .eq(SysUser::getDelFlag, DelFlagEnum.NO.getCode()));

    // 获取用户IP信息
    HttpServletRequest request =
        ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
    String ipAddress = "0.0.0.0";

    if (Objects.isNull(sysUser)) {
      log.info("登录用户：{}不存在", req.getUsername());
      amsEventPublisher.publishEvent(
          new LoginLogEvent("修改密码", 2, req.getUsername(), "修改密码", "登录用户不存在"));
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "登录用户不存在！");
    }
    if (!SecurityUtil.matchesPassword(req.getOldPassword(), sysUser.getPassword())) {
      amsEventPublisher.publishEvent(
          new LoginLogEvent("修改密码", 2, req.getUsername(), "修改密码", "账号或密码不正确"));
      throw BizTipException.instance(ErrorCode.VALIDATE_ERROR, "账号或密码不正确！");
    }
    sysUser.setPassword(SecurityUtil.encryptPassword(req.getPassword()));
    sysUserMapper.updateById(sysUser);
  }

  @Override
  public AuthPermissionVO getPermissionInfo() {
    AuthPermissionVO permissionInfo = new AuthPermissionVO();
    // 获取权限集合
    Set<String> permissions = SecurityUtil.getLoginUser().getPermissions();
    permissionInfo.setPermissions(permissions);
    // 获取角色集合
    List<SysRoleBO> roleList = SecurityUtil.getSysUser().getRoles();
    Set<String> roles = roleList.stream().map(SysRoleBO::getRoleCode).collect(Collectors.toSet());
    permissionInfo.setRoles(roles);
    // 获取用户信息
    SysUserBO sysUser = SecurityUtil.getSysUser();
    permissionInfo.setUser(sysUser);
    // 菜单
    ISysMenuSvc sysMenuService = SpringUtil.getBean(ISysMenuSvc.class);
    List<RouteItemVO> menus = sysMenuService.getMenuList();
    permissionInfo.setMenus(menus);
    return permissionInfo;
  }
}
